#!/usr/bin/env python3

import sys
import queue
import logging
import threading
import multiprocessing as mp



class Logger(logging.Logger):
    LEVELS = [
        logging.FATAL, logging.ERROR, logging.WARN,
        logging.INFO, logging.DEBUG
    ]

    def __init__(self, name, level):
        if level > 5 or level < 1:
            print('log level between 1~5')
            sys.exit(0)
        logging.Logger.__init__(self, name, Logger.LEVELS[level - 1])

    def add_stream(self, path=None, level=logging.DEBUG):
        if path:
            hdlr = logging.FileHandler(path)
        else:
            hdlr = logging.StreamHandler(sys.stderr)
        fmt = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
        hdlr.setFormatter(fmt)
        hdlr.setLevel(level)
        self.addHandler(hdlr)


class SpiderBase(object):
    _defaultlogger = None

    def __init__(self, logger=None):
        self.logger = logger if logger else SpiderBase._defaultlogger


class ThreadPoolWorker(threading.Thread, SpiderBase):
    def __init__(self, work_queue, result_queue=None):
        threading.Thread.__init__(self)
        SpiderBase.__init__(self)
        self.work_queue = work_queue
        self.result_queue = result_queue

    def run(self):
        while True:
            try:
                fn, args, kwargs = self.work_queue.get()
                result = fn(*args, **kwargs)
                if self.result_queue:
                    self.result_queue.put(result)
                self.work_queue.task_done()
            except Exception as e:
                self.logger.error('%s occured on %s when doing %s',
                                  str(e), self.name, fn.__name__)


class ThreadPool(object):
    def __init__(self, max_workers, max_tasks):
        self.tasks = queue.Queue(max_tasks)
        self.threads = []
        self.__create_threads(max_workers)

    def __create_threads(self, max_workers):
        for i in range(max_workers):
            t = ThreadPoolWorker(self.tasks)
            t.daemon = True
            t.start()
            self.threads += [t]

    def submit(self, fn, *args, **kwargs):
        self.tasks.put((fn, args, kwargs))

    def wait(self):
        for t in self.threads:
            if t.is_alive():
                t.join()
